#ifndef dnsclient_h
#define dnsclient_h

#include <sys/types.h>

#define BUFFERSIZE		1024
#define DEFAULT_QSIZE		1024
#define MAX_QSIZE		32768
#define DEFAULT_ASIZE		2048

#define RESOLV_CONF		"/etc/resolv.conf"
#define PORT			53
#define DEFAULT_SERVER          "4.2.2.2"

#define DEFAULT_DNS_CLASS	DNS_C_IN

/* DNS supported classes */
typedef enum dns_class {
    DNS_C_IN	= 1,	// Internet class
    DNS_C_CS	= 2,	// CSNET class 
    DNS_C_CH	= 3,	// Chaosnet class
    DNS_C_HS	= 4,	// Hesiod class
    DNS_C_ANY	= 255,	// Any other class
} dns_class_t;

/* DNS supported rr(resource record) types */
typedef enum dns_type {
    DNS_T_A	= 1,	// host address
    DNS_T_NS	= 2,	// authoritative name server
    DSN_T_MD	= 3,	// mail destination (obsoleted)
    DSN_T_MF	= 4,	// mail forwarder (obsoleted)
    DNS_T_CNAME	= 5,	// canonical name for an alias
    DNS_T_SOA	= 6,	// start of zone of autoritative
    DSN_T_MB	= 7,	// mail box doamain name (experimental)
    DSN_T_MG	= 8,	// mail group member (experimental)
    DSN_T_MR	= 9,	// mail rename domain name (experimental)
    DNS_T_NULL	= 10,	// NULL record (experimental)
    DNS_T_WKS	= 11,	// well known service description
    DNS_T_PTR	= 12,	// domain name pointer
    DNS_T_HINFO	= 13,	// host information
    DSN_T_MX	= 15,	// mail exchange
    DNS_T_TXT	= 16,	// text strings
    DNS_T_AAAA	= 28,	// ipv6 host address
    DNS_T_SRV	= 33,	// srv record
    DNS_T_ANY	= 255,	// any other
} dns_type_t;

/* DNS rr of type MD and MF are not required as thay are obosoleted so not declaring it here */
/* DNS rr of type MG and MR are not required as thay are experimental so not declaring it here */
/* DNS rr of type WKS is not required so not declaring it here */

/* DNS rr of type A */
typedef struct dns_rr_a {
    u_int32_t address;	// address of a record
} dns_rr_a_t;

/* DNS rr of type of NS */
typedef struct dns_rr_ns {
    const char *nsdname;
} dns_rr_ns_t;

/* DNS rr of type CNAME */
typedef struct dns_rr_cname {
    const char *name;
} dns_rr_cname_t;

/* DNS rr of type SOA */
typedef struct dns_rr_soa {
    const char *mname;	// domain name of name server
    const char *rname;	// admin email address
    u_int32_t serial;	// version no of original copy of the zone
    int32_t refresh;	// refresh interval before the zone should be refreshed
    int32_t retry;	// retry interval after failed refresh 
    int32_t expire;	// upper limit on the time interval that can elapse before the zone is no longer  autoritative
    int32_t minimum;	// minumum TTL for any rr from the respected zone
} dns_rr_soa_t;

/* DNS rr of type NULL */
typedef struct dns_rr_null {
    unsigned const char *data;
} dns_rr_null_t;

/* DNS rr of type PTR */
typedef struct dns_rr_ptr {
    const char *ptrdname;
} dns_rr_ptr_t;

/* DNS rr of type HINFO */
typedef struct dns_rr_hinfo {
    const char *cpu, *os;
} dns_rr_hinfo_t;

/* DNS rr of type MX */
typedef struct dns_rr_mx {
    u_int16_t preference;
    const char *exchange;
} dns_rr_mx_t;

/* DNS rr of type TXT */
typedef struct dns_rr_txt {
    const char *data;
} dns_rr_txt_t;

/* DNS rr of type AAAA */
typedef struct dns_rr_aaaa {
    uint32_t oct[4];	// ipv6 address of a record
} dns_rr_aaaa_t;

/* DNS rr of type SRV */
typedef struct dns_rr_srv {
    const char *server;
    u_int16_t priority;
    u_int16_t weight;
    u_int16_t port;
} dns_rr_srv_t;

/* DNS rr defination */
typedef struct dns_rr {
    const char *dns_name;
    enum dns_type dns_type;
    enum dns_class dns_class;
    int32_t dns_ttl;
    u_int16_t dns_rlength;
    union {
	struct dns_rr_a a;
	struct dns_rr_ns ns;
	struct dns_rr_cname cname;
	struct dns_rr_soa soa;
	struct dns_rr_null null;
	struct dns_rr_ptr ptr;
	struct dns_rr_hinfo hinfo;
	struct dns_rr_mx mx;
	struct dns_rr_txt txt;
	struct dns_rr_aaaa aaaa;
	struct dns_rr_srv srv;
    } dns_rdata;
} dns_rr_t;

/* defination for dns query header */
typedef struct dns_query_header {
    u_int16_t dns_id;
    struct {
	unsigned char dns_bits_rd: 1;		// recursion desired
	unsigned char dns_bits_tc: 1;		// truncation
	unsigned char dns_bits_aa: 1;		// autoritative answer
	unsigned char dns_bits_opcode: 4;	// query type
	unsigned char dns_bits_qr: 1;		// query response
    } dns_bits1;
    struct {
	unsigned char dns_bits_rcode: 4;	// response code
	unsigned char dns_bits_z: 3;		// reserved
	unsigned char dns_bits_ra: 1;		// recursion available
    } dns_bits2;
    u_int16_t dns_qdcount;			// number of queries
    u_int16_t dns_ancount;			// number of answers
    u_int16_t dns_nscount;			// number of nameservers
    u_int16_t dns_arcount;			// number of additionam resource records
} dns_query_header_t;

#define dns_rd		dns_bits1.dns_bits_rd
#define dns_tc		dns_bits1.dns_bits_tc
#define dns_aa		dns_bits1.dns_bits_aa
#define dns_opcode	dns_bits1.dns_bits_opcode
#define dns_qr		dns_bits1.dns_bits_qr

#define dns_rcode	dns_bits2.dns_bits_rcode
#define dns_z		dns_bits2.dns_bits_z
#define dns_ra		dns_bits2.dns_ra

/* need to work on it so only query structure need to be porcess to generate dns query */
typedef struct dns_query {
    struct dns_query_header header;
    u_int16_t dns_type;
    u_int16_t dns_class;
} dns_query_t;

typedef struct dc_addrinfo {
    int ai_family;
    size_t ai_addrlen;
    struct sockaddr *ai_addr;
    struct dc_addrinfo *ai_next;
} dc_addrinfo_t;

/* send query to dns server and get result (call directly from client) */
struct dc_addrinfo * dns_client_query(const char *, u_int16_t, int);

/* format qns query in DNS packet form with header and label  (called from dns_client_query) */ 
size_t dns_format_query(const char *, dns_class_t, dns_type_t, unsigned char *, size_t);

/* convert query name to label for dns query */
size_t dns_name_to_label(const char *, unsigned char *, size_t);

/* parse dns results in user readable format */
struct dc_addrinfo * dns_parse_results(const unsigned char *, size_t);

/* parse label to the user readable form */
size_t dns_parse_label(const unsigned char *base, size_t base_length, const unsigned char *label, unsigned char *output, size_t output_length);

/* generate linked list like addrinfo */
void add_addrinfo(struct dc_addrinfo **, uint32_t *, int);

/* free generated linked list */
void free_addrinfo(struct dc_addrinfo **);

#endif
